package proxy;

import static org.springframework.web.servlet.HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;

import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.ServletRequestDataBinderFactory;
import org.springframework.web.util.WebUtils;

import configuration.RequestProxyConfiguration;
import configuration.SpringContextUtil;

/**
 * class LocalRequestProxy
 *
 * @author alex
 * @version 0.1
 * @since 2017 12/13/17
 */
public class LocalRequestProxy implements IRequestProxy {

	private RequestMappingHandlerMapping handlerMapping;
	private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
	private Logger logger = LoggerFactory.getLogger(RequestProxyConfiguration.class);

	public LocalRequestProxy(RequestMappingHandlerAdapter requestMappingHandlerAdapter,
			RequestMappingHandlerMapping requestMappingHandlerMapping) {
		this.handlerMapping = requestMappingHandlerMapping;
		this.requestMappingHandlerAdapter = requestMappingHandlerAdapter;
	}

	@Override
	public <T> T get(String url, Map<Object, ?> params, Map<Object, ?> urlParams, Type type) {
		url = getUri(url);
		String value = "";
		if (url != null) {
			value = this.call(url, params, urlParams, RequestMethod.GET.toString());
		}
		return SpringContextUtil.getValue(value, type);
	}

	@Override
	public <T> T post(String url, Map<Object, ?> params, Map<Object, ?> urlParams, Type type) {
		String uri = getUri(url);
		String value = null;
		if (uri != null) {
			value = this.call(uri, params, urlParams, RequestMethod.POST.toString());
		}
		return SpringContextUtil.getValue(value, type);
	}

	@Override
	public void del(String url, Map<Object, ?> params) {

	}

	private String call(String url, Map<Object, ?> params, Map<Object, ?> urlParams, String method) {
		Object value = null;
		// 获取指定url的处理方法
		ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder
				.getRequestAttributes();
		HttpServletRequest request = servletRequestAttributes.getRequest();
		CustomerHttpServletRequest customerHttpServletRequest = new CustomerHttpServletRequest(request);
		// 增加参数
		customerHttpServletRequest.addAllParameters(params);
		// 设置请求方法
		customerHttpServletRequest.setMethodName(method);
		// 设置uri
		customerHttpServletRequest.setUri(url);
		// 设置servlet请求路径 解析方法参数是 使用
		customerHttpServletRequest.setAttribute(WebUtils.INCLUDE_SERVLET_PATH_ATTRIBUTE, url);
		if (urlParams != null && !urlParams.isEmpty()) {
			// url中变量
			Map<Object, Object> temMap = (Map<Object, Object>) customerHttpServletRequest
					.getAttribute(URI_TEMPLATE_VARIABLES_ATTRIBUTE);
			if (temMap == null) {
				temMap = new LinkedHashMap<>();
			}
			for (Map.Entry<Object, ?> entry : urlParams.entrySet()) {
				temMap.put(entry.getKey().toString(), entry.getValue().toString());
			}
		}
		HandlerMethod handlerMethod = getHandlerMethod(customerHttpServletRequest);
		if (handlerMethod == null) {
			throw new RuntimeException("未找到 " + url + " 的处理方法");
		}
		ServletWebRequest servletWebRequest = new ServletWebRequest(customerHttpServletRequest);
		CustomerInvocableHandlerMethod customerInvocableHandlerMethod = new CustomerInvocableHandlerMethod(
				handlerMethod);
		HandlerMethodArgumentResolverComposite handlerMethodArgumentResolverComposite = new HandlerMethodArgumentResolverComposite();
		// 设置方法参数解析方法
		handlerMethodArgumentResolverComposite.addResolvers(requestMappingHandlerAdapter.getArgumentResolvers());
		customerInvocableHandlerMethod.setHandlerMethodArgumentResolvers(handlerMethodArgumentResolverComposite);
		WebDataBinderFactory binderFactory = new ServletRequestDataBinderFactory(new ArrayList<>(),
				requestMappingHandlerAdapter.getWebBindingInitializer());
		// 设置方法参数中有实体接收的构造工厂
		customerInvocableHandlerMethod.setDataBinderFactory(binderFactory);
		// 反射调用
		try {
			value = customerInvocableHandlerMethod.invokeForRequest(servletWebRequest, new ModelAndViewContainer());
		} catch (Exception e) {
			logger.error(e.getMessage());
			e.printStackTrace();
			value = "{\"state\":\"Failure\",\"message\":\"本地调用失败\",\"data\":null}";
		}
		return value.toString();
	}

	/**
	 * 根据路径获取处理方法
	 */
	private HandlerMethod getHandlerMethod(HttpServletRequest request) {
		if (request.getRequestURI().contains("{") || request.getRequestURI().contains("}")) {
			return getHandlerMethod_rest(request);
		} else {
			return getHandlerMethod_match(request);
		}
	}

	/**
	 * 根据路径获取处理方法
	 */
	private HandlerMethod getHandlerMethod_match(HttpServletRequest request) {
		Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = handlerMapping.getHandlerMethods();
		RequestMappingInfo info = null;
		OK: for (RequestMappingInfo requestMappingInfo : handlerMethodMap.keySet()) {
			Set<String> urlSet = requestMappingInfo.getPatternsCondition().getPatterns();
			for (String url : urlSet) {
				if (url.equalsIgnoreCase(request.getRequestURI())) {
					info = requestMappingInfo;
					break OK;
				}
			}
		}
		return handlerMethodMap.get(info);
	}

	/**
	 * 根据路径获取处理方法
	 */
	private HandlerMethod getHandlerMethod_rest(HttpServletRequest request) {
		Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = handlerMapping.getHandlerMethods();
		RequestMappingInfo info = null;
		for (RequestMappingInfo requestMappingInfo : handlerMethodMap.keySet()) {
			info = requestMappingInfo.getMatchingCondition(request);
			if (info != null) {
				break;
			}
		}
		return handlerMethodMap.get(info);
	}

	/**
	 * 根据url 获取uri
	 */
	private String getUri(String url) {
		try {
			URL url1 = new URL(url);
			return url1.getPath();
		} catch (MalformedURLException e) {
			logger.error(e.getMessage());
			e.printStackTrace();
		}
		return null;
	}
}
